#ifndef MAIN_WIDGET_H
#define MAIN_WIDGET_H

#include <QWidget>
#include <QAbstractSocket>
class QTcpSocket;
class QFile;

namespace Ui {
class main_widget;
}

class main_widget : public QWidget
{
    Q_OBJECT

public:
    explicit main_widget(QWidget *parent = nullptr);
    ~main_widget();

private slots:
    void on_link_pushButton_toggled(bool checked);

    void on_choose_pushButton_clicked();

    void on_start_pushButton_clicked();

    // 手动添加的私有函数

    /************************************************************************
    函数名称: calc_crc16
    函数功能: 计算N字节的Crc校验
    函数参数: user_data: 需要计算CRC的数据
              user_data_len: 需要计算CRC的数据长度
    函数返回: 计算得到的CRC
    ************************************************************************/
    unsigned short calc_crc16(const unsigned char *user_data,
                              unsigned long user_data_len);

    /************************************************************************
    函数名称: read_file
    函数功能: 读取文件内容
    函数参数: file_path: 文件路径
              position: 读取内容的起始位置
              len: 指定读取的长度
              read_buf: 读取到的文件内容
    函数返回: 成功: 返回实际读取到的文件长度
              失败: 返回-1
    ************************************************************************/
    qint64 read_file(QString file_path, const unsigned long position,
                     const unsigned long len, char *read_buf);

    /************************************************************************
    函数名称: recv_data_parsing
    函数功能: 接收数据解析
    函数参数: recv_data: 接收到的数据
              recv_data_len: 接收数据长度
              recv_data_type: 接收到的有效数据类型
    函数返回: 成功: 返回0(RECV_OK)
              失败: 返回对应错误码(详见enum transfer_response)
    ************************************************************************/
    int recv_data_parsing(const unsigned char *recv_data,
                          const unsigned long recv_data_len,
                          unsigned char *recv_data_type);

    /************************************************************************
    函数名称: send_data_package
    函数功能: 发送数据打包
    函数参数: send_data_type: 发送数据类型
              send_user_data: 需要发送的用户数据
              send_user_data_len: 需要发送的用户数据长度
              send_data: 打包完成的数据
              send_data_len: 打包完成的数据长度
    函数返回: 无
    ************************************************************************/
    void send_data_package(const unsigned char send_data_type,
                           const char *send_user_data,
                           const unsigned short send_user_data_len,
                           char *send_data, unsigned long *send_data_len);

    /************************************************************************
    函数名称: file_transfer_function
    函数功能: 文件传输函数
    函数参数: 无
    函数返回: 无
    ************************************************************************/
    int file_transfer_function(void);

private:
    Ui::main_widget *ui;

    // 手动添加的私有变量
    #define     CLIENT_HEAD             0xAA        // 客户端发送的消息头
    #define     SERVER_HEAD             0xBB        // 服务器发送的消息头
    #define     MAX_USER_DATA_LEN       1024        // 传输用户数据最大长度(不包括CRC)
    #define     MAX_FILE_NAME_LEN       50          // 支持的最大文件名长度

    // 定义传输数据类型
    enum transfer_data_type
    {
        TRANSFER_FILE_NAME = 0xA0,      // 传输文件名
        TRANSFER_FILE_SIZE = 0xA1,      // 传输文件大小
        TRANSFER_FILE_DATA = 0xA2,      // 传输文件内容
        TRANSFER_FILE_END = 0xA3        // 传输文件结束
    };

    // 传输文件内容应答类型
    enum transfer_response
    {
        RECV_OK = 0x00,             // 接收成功
        HEAD_ERR = 0x01,            // 帧头错误
        CRC_ERR = 0x02,             // CRC错误
        TRANSFER_OK = 0x03,         // 文件传输成功
        TRANSFER_FAIL = 0x04,       // 文件传输失败
        OTHER_ERR = 0x0A            // 其它错误
    };

    #pragma pack(1)
    // 传输协议结构体
    struct file_transfer
    {
        unsigned char frame_head;       // 帧头
        unsigned char frame_number;     // 帧序号
        unsigned char user_data_type;   // 用户数据类型(详见enum transfer_data_type)
        unsigned short user_data_len;   // 用户数据长度
        unsigned char user_data[MAX_USER_DATA_LEN + 2];     // 用户数据(CRC附加到数据末2字节)
    };

    // 记录文件信息
    struct file_info
    {
        char file_name[MAX_FILE_NAME_LEN];      // 文件名
        qint64 file_size;       // 文件大小
    };

    #pragma pack()

    QTcpSocket *tcp_client;
    QString file_path;      // 文件路径
    QFile *local_file;      // 要发送的文件
    struct file_info t_file_info;       // 记录文件信息
    unsigned char g_frame_number;       // 上传数据中的帧序号(应答时也回应相同帧序号)
    unsigned char send_file_name_flag = 0;   // 发送文件名成功标志位
    unsigned char send_file_size_flag = 0;   // 发送文件大小成功标志位
    unsigned char send_file_data_flag = 0;   // 发送文件内容结束标志位
    unsigned long read_data_position = 0;    // 记录读取文件内容位置
};

#endif // MAIN_WIDGET_H
